package serie02;

import java.io.File;
import java.io.IOException;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;

public class StdSplitManager implements SplitManager {
	
	// ATTRIBUTS
	
	private File file;
	private long[] splitsSize;
	
	// CONSTRUCTEUR
	
	/**
	 * Un gestionnaire de scission bas� sur le fichier file.
	 * @pre
	 *     f != null
	 * @post
	 *     getFileName().equals(file.getAbsolutePath()
	 *     getFileSize() == file.length()
	 *     getSplitsSizes().length <= 1 
	 */
	public StdSplitManager(File f) {
		if (f == null) {
			throw new IllegalArgumentException("Mauvais fichier");
		}
		file = f;
		splitsSize = new long[] {};
	}
	
	// REQUETES
	
	public String getFileName() {
		return file.getName();
	}
	
	public long getFileSize() {
		return file.length();
	}
	
	public String[] getSplitsNames() {
		String[] splitsName = new String[getSplitsSizes().length];
		for (int i = 0; i < getSplitsSizes().length; i++) {
			splitsName[i] = getFileName() + "." + (i + 1);
		}
		return splitsName;
	}
	
	public long[] getSplitsSizes() {
		long[] temp = new long[splitsSize.length];
		for (int i = 0; i < splitsSize.length; i++) {
			temp[i] = splitsSize[i];
		}
		return temp; 
	}
	
	// COMMANDES
	
	public void setSplitsSizes(long newSize) {
		if (newSize < 1) {
			throw new IllegalArgumentException(
				"Taille des fragments invalide"
			);  
		}
		if (getFileSize() / newSize > Integer.MAX_VALUE) {
			throw new IllegalArgumentException("Trop de fragments");
		}
		int nbSplits;
		if (getFileSize() % newSize == 0) {
			nbSplits = (int) (getFileSize() / newSize);
		} else {
			nbSplits = (int) (getFileSize() / newSize + 1);
		}
		splitsSize = new long[nbSplits];
		for (int i = 0; i < nbSplits - 1; i++) {
			splitsSize[i] = newSize;
		}
		if (getFileSize() > 0) {
			splitsSize[nbSplits - 1] = getFileSize() 
										- (nbSplits - 1) * newSize;
		}
	}
	
	public void setSplitsSizes(long[] newSizes) {
		if (newSizes == null) {
			throw new IllegalArgumentException("Tableau inexistant");
		}
		if (newSizes.length < 1) {
			throw new IllegalArgumentException("Tableau vide");
		}
		for (long x : newSizes) {
			if (x < 1) {
				throw new IllegalArgumentException("Tableau incorrect");
			}
		}
		// sum correspond à la somme des éléments du tableau newSizes
		long sum = 0;
		for (long x : newSizes) {
			sum += x;
		}
		if (getFileSize() <= sum) {
			// On tronque le tableau newSizes
			long accu = 0;
			// nbFiles correspond au nombre de fichiers à créer
			int nbFiles = 0;
			int j = 0;
			while (accu < getFileSize()) {
				nbFiles++;
				accu += newSizes[j];
				j++;
			}
			splitsSize = new long[nbFiles];
			// sum2 correspond à la longueur totale des fichiers sans le dernier
			long sum2 = 0;
			for (int i = 0; i < nbFiles - 1; i++) {
				splitsSize[i] = newSizes[i];
				sum2 += newSizes[i];
			}
			if (getFileSize() > 0) {
				splitsSize[nbFiles - 1] = getFileSize() - sum2;
			}
		} else {
			splitsSize = new long[newSizes.length + 1];
			for (int i = 0; i < newSizes.length; i++) {
				splitsSize[i] = newSizes[i];
			}
			splitsSize[newSizes.length] = getFileSize() - sum;
		}
	}
	
	public void setSplitsNumber(int number) {
		if (number < 1) {
			throw new IllegalArgumentException("Mauvais nombre de fragments");
		}
		if (getFileSize() < number) {
			splitsSize = new long[(int) getFileSize()];
			for (int i = 0; i < splitsSize.length; i++) {
				splitsSize[i] = 1;
			}
		} else {
			splitsSize = new long[number];
			for (int i = 0; i < (getFileSize() % number); i++) {
				splitsSize[i] = getFileSize() / number + 1;
			}
			for (int i = (int) (getFileSize() % number); i < number; i++) {
				splitsSize[i] = getFileSize() / number;
			}
		}
	}
	
	public void split() throws IOException {
		if (getFileSize() != 0) {
			FileInputStream f = new FileInputStream(file);
			BufferedInputStream br = new BufferedInputStream(f);
			for (int i = 0; i < splitsSize.length; i++) {
				FileOutputStream fich = 
						new FileOutputStream(getSplitsNames()[i]);
				BufferedOutputStream bf = new BufferedOutputStream(fich);
				byte[] tab = new byte[(int) getSplitsSizes()[i]];
				// Lecture du fichier à scinder
				br.read(tab);
				// Ecriture dans un fichier
				bf.write(tab);
				bf.close();
			}
			br.close();
		}
	}

}

